home *** CD-ROM | disk | FTP | other *** search
/ NeXT Education Software Sampler 1992 Fall / NeXT Education Software Sampler 1992 Fall.iso / Programming / Source / HippoDraw / HippoDrawSrc1.1 / Hippo.subproj / Polygon.m < prev    next >
Encoding:
Text File  |  1992-04-25  |  6.7 KB  |  208 lines

  1. #import "Polygon.h"
  2. #import "GraphicView.h"
  3. #import <appkit/Application.h>
  4. #import <appkit/Window.h>
  5. #import <appkit/nextstd.h>
  6. #import <dpsclient/wraps.h>
  7. #import <math.h>
  8.  
  9. @implementation Polygon
  10.  
  11. + cursor
  12. /*
  13.  * The cursor inherited from Scribble is a pencil.
  14.  * That's not very appropriate, so CrossCursor is used instead.
  15.  */
  16. {
  17.     return CrossCursor;
  18. }
  19.  
  20. static void getRectFromBBox(NXRect *r, float x1, float y1, float x2, float y2)
  21. /*
  22.  * Takes two points (x1, y1) and (x2, y2) and updates the r rect to
  23.  * equal that bounding box.
  24.  */
  25. {
  26.     r->size.width = x1 - x2;
  27.     r->size.height = y1 - y2;
  28.     if (r->size.width < 0.0) {
  29.     r->origin.x = x2 + r->size.width;
  30.     r->size.width = 0.0 - r->size.width;
  31.     } else r->origin.x = x2;
  32.     if (r->size.height < 0.0) {
  33.     r->origin.y = y2 + r->size.height;
  34.     r->size.height = 0.0 - r->size.height;
  35.     } else r->origin.y = y2;
  36. }
  37.  
  38. /*
  39.  * This class probably is probably not implemented in the optimal way,
  40.  * but it shows how an existing implementation (i.e. Scribble) can be
  41.  * used to implement some other object.
  42.  *
  43.  * This method creates a polygon.  The user must drag out each segment of
  44.  * the polygon clicking to make a corner, finally ending with a double click.
  45.  *
  46.  * Start by getting the starting point of the polygon from the mouse down
  47.  * event passed in the event parameter (if the ALT key is not down, then we
  48.  * will close the path even if the user does not explicitly do so).
  49.  *
  50.  * Next, we initialize a chunk of space for the points to be stored in
  51.  * and initialize point[0] and point[1] to be the starting point (since the
  52.  * first thing in the userpath is a moveto).  We also initialize our bounding
  53.  * box to contain only that point.
  54.  *
  55.  * p represents the last point the user moved the mouse to.  We initialize it
  56.  * to start before entering the tracking loop.
  57.  *
  58.  * Inside the loop, last represents the last point the user confirmed (by
  59.  * clicking) as opposed to p, the last point the user moved to.  We update
  60.  * last every time we start the segment tracking loop (the inner,
  61.  * while (event->type != NX_MOUSEUP) loop).
  62.  *
  63.  * In the segment tracking loop, r represents the rectangle which must be
  64.  * redrawn to get rid of the last time we drew the segment we are currently
  65.  * tracking.  After we [view drawSelf:&r :1] to clear out the last segment,
  66.  * we recalculate the value of r for the next time around the loop.  Finally,
  67.  * we draw ourselves (i.e. all the other segments besides the one we are
  68.  * currently tracking) and then draw the segment we are currently tracking.
  69.  *
  70.  * After tracking the segment, we check to see if we are done.
  71.  * We are finished if any of the following are true:
  72.  *    1. The last segment the user created was smaller than a gridSpacing.
  73.  *    2. The user clicked on the starting point (thereby closing the path).
  74.  *    3. The mouse down is outside the view's bounds.
  75.  *    4. A kit defined or system defined event comes through.
  76.  *
  77.  * If we are not done (or we need to close the path), then we store the
  78.  * new point pair into points (reallocating our points
  79.  * and userPathOps arrays if we are out of room).  We then update our bounding
  80.  * box to reflect the new point and update our bounds to equal our bounding
  81.  * box.  If we aren't done, we look for the next mouse down to begin the
  82.  * tracking of another segment.
  83.  *
  84.  * After we are finished with all segments, we check to be sure that we have
  85.  * at least two segments (one segment is a line, not a polygon).  If the
  86.  * path is closed, then we need at least three segments.  If we have the
  87.  * requisite number of segments, then we reallocate our arrays to fit exactly
  88.  * our number of points and return YES.  Otherwise, we free the storage of
  89.  * those arrays and clean up any drawing we did and return NO.
  90.  */
  91.  
  92. #define POLYGON_MASK (NX_MOUSEDRAGGEDMASK|NX_MOUSEUPMASK)
  93. #define END_POLYGON_MASK (NX_KITDEFINEDMASK|NX_MOUSEDOWNMASK|NX_SYSDEFINEDMASK)
  94.  
  95. - (BOOL)create:(NXEvent *)event in:view
  96. {
  97.     float *pptr;
  98.     NXRect r, viewBounds;
  99.     NXPoint start, last, p;
  100.     id window = [view window];
  101.     BOOL closepath, done = NO, resend = NO;
  102.     float grid = (float)[view gridSpacing];
  103.     int windowNum = event->window, arrow = 0;
  104.  
  105.     gFlags.initialized = YES;
  106.     if (gFlags.arrow && gFlags.arrow != ARROW_AT_START) {
  107.     arrow = gFlags.arrow;
  108.     gFlags.arrow = (gFlags.arrow == ARROW_AT_END) ? 0 : ARROW_AT_START;
  109.     }
  110.  
  111.     start = event->location;
  112.     [view convertPoint:&start fromView:nil];
  113.     [view grid:&start];
  114.  
  115.     [view getVisibleRect:&viewBounds];
  116.  
  117.     closepath = (event->flags & NX_ALTERNATEMASK) ? NO : YES;
  118.  
  119.     length = 0;
  120.     [self allocateChunk];
  121.     pptr = points;
  122.     *pptr++ = bbox[0] = bbox[2] = start.x;
  123.     *pptr++ = bbox[1] = bbox[3] = start.y;
  124.     userPathOps[0] = dps_moveto;
  125.  
  126.     [view lockFocus];
  127.  
  128.     [self setLineColor];
  129.     PSsetlinewidth(linewidth);
  130.  
  131.     p = start;
  132.     event = [NXApp getNextEvent:POLYGON_MASK];
  133.     while (!done) {
  134.     last = p;
  135.     if (event->type == NX_MOUSEDOWN) {
  136.         if (event->data.mouse.click > 1) {
  137.         done = YES;
  138.         [NXApp getNextEvent:NX_MOUSEUPMASK];
  139.         } else if (event->window != windowNum) {
  140.         done = YES;
  141.         resend = YES;
  142.         } else {
  143.         p = event->location;
  144.         [view convertPoint:&p fromView:nil];
  145.             done = !NXMouseInRect(&p, &viewBounds, NO);
  146.         resend = YES;
  147.         }
  148.     } else if (event->type == NX_KITDEFINED ||
  149.            event->type == NX_SYSDEFINED) {
  150.         done = YES;
  151.         resend = YES;
  152.     }
  153.     if (!done) {
  154.         while (event->type != NX_MOUSEUP) {
  155.         p = event->location;
  156.         [view convertPoint:&p fromView:nil];
  157.         [view grid:&p];
  158.         [view drawSelf:&r :1];
  159.         getRectFromBBox(&r, p.x, p.y, last.x, last.y);
  160.         [view scrollPointToVisible:&p];
  161.         NXInsetRect(&r, -2.0, -2.0);
  162.         [self draw];
  163.         PSmoveto(last.x, last.y);
  164.         PSlineto(p.x, p.y);
  165.         PSstroke();
  166.         [window flushWindow];
  167.         event = [NXApp getNextEvent:POLYGON_MASK];
  168.         }
  169.         if (fabs(p.x-start.x) <= grid && fabs(p.y-start.x) <= grid) {
  170.         done = YES;
  171.         closepath = YES;
  172.         }
  173.     }
  174.     if (!done || (closepath && length > 1)) {
  175.         if (done) p = start;
  176.         length++;
  177.         if (!(length % CHUNK_SIZE)) [self allocateChunk];
  178.         *pptr++ = p.x - last.x;
  179.         *pptr++ = p.y - last.y;
  180.         if (p.x < bbox[0]) bbox[0] = p.x;
  181.         if (p.x > bbox[2]) bbox[2] = p.x;
  182.         if (p.y < bbox[1]) bbox[1] = p.y;
  183.         if (p.y > bbox[3]) bbox[3] = p.y;
  184.         getRectFromBBox(&bounds, bbox[0], bbox[1], bbox[2], bbox[3]);
  185.         if (!done) event = [NXApp getNextEvent:END_POLYGON_MASK];
  186.     }
  187.     }
  188.  
  189.     [view unlockFocus];
  190.  
  191.     if (resend) DPSPostEvent(event, 1);
  192.     if (arrow) gFlags.arrow = arrow;
  193.  
  194.     if (length > (closepath ? 2 : 1)) {
  195.     points = NX_ZONEREALLOC([self zone], points, float, (length+1) << 1);
  196.     userPathOps = NX_ZONEREALLOC([self zone], userPathOps, char, length+1);
  197.     return YES;
  198.     } else {
  199.     NX_FREE(points); points = NULL;
  200.     NX_FREE(userPathOps); userPathOps = NULL;
  201.     [view drawSelf:[self getExtendedBounds:&r] :1];
  202.     return NO;
  203.     }
  204. }
  205.  
  206. @end
  207.  
  208.